Skip to content

Conversation

@mk3008
Copy link
Owner

@mk3008 mk3008 commented Oct 19, 2025

  • Added methods to build DELETE and MERGE queries in QueryBuilder.
  • Implemented normalization functions for DELETE and MERGE options.
  • Updated existing methods to hoist CTEs and handle explicit columns.
  • Introduced tests for buildDeleteQuery, buildInsertQuery, and buildMergeQuery to ensure correctness.
  • Refactored existing update and insert query methods to improve column handling and error messaging.

Summary by CodeRabbit

  • New Features

    • Added public conversions to turn SELECTs into INSERT, UPDATE, DELETE, and MERGE queries with configurable options.
    • New utilities to convert between INSERT ... VALUES and INSERT ... SELECT (UNION ALL).
    • Query builder and SQL formatter extended to support MERGE construction and printing.
  • Tests

    • Comprehensive tests for conversions and builders, including round-trip conversions, MERGE/DELETE/UPDATE scenarios, and extensive error handling.

- Added methods to build DELETE and MERGE queries in QueryBuilder.
- Implemented normalization functions for DELETE and MERGE options.
- Updated existing methods to hoist CTEs and handle explicit columns.
- Introduced tests for buildDeleteQuery, buildInsertQuery, and buildMergeQuery to ensure correctness.
- Refactored existing update and insert query methods to improve column handling and error messaging.
@coderabbitai
Copy link

coderabbitai bot commented Oct 19, 2025

Walkthrough

Adds conversion APIs to transform SELECT queries into INSERT, UPDATE, DELETE, and MERGE queries, extends QueryBuilder with new buildXQuery methods, introduces an InsertQuery <-> VALUES transformer, and adds tests exercising conversions and error cases.

Changes

Cohort / File(s) Summary
Model: SelectQuery types & exports
packages/core/src/models/SelectQuery.ts
Adds conversion option interfaces (InsertQueryConversionOptions, UpdateQueryConversionOptions, DeleteQueryConversionOptions, MergeQueryConversionOptions) and merge action types; extends SelectQuery interface with toInsertQuery, toUpdateQuery, toDeleteQuery, toMergeQuery; exports InsertQuery, UpdateQuery, DeleteQuery, MergeQuery.
Query classes — conversion methods
packages/core/src/models/SimpleSelectQuery.ts, packages/core/src/models/BinarySelectQuery.ts, packages/core/src/models/ValuesQuery.ts
Adds toInsertQuery, toUpdateQuery, toDeleteQuery, toMergeQuery implementations. SimpleSelectQuery delegates to QueryBuilder.buildXQuery; BinarySelectQuery and ValuesQuery delegate via toSimpleQuery. Adds type-only imports for conversion options and result query types.
Query builder implementations
packages/core/src/transformers/QueryBuilder.ts
Expands signatures for buildInsertQuery and buildUpdateQuery to accept option objects; adds new buildDeleteQuery and buildMergeQuery methods. Implements option normalization, column inference/validation, predicate/from clause construction, and merge plan generation. Enhances error handling and CTE hoisting.
Insert <-> VALUES transformer
packages/core/src/transformers/InsertQuerySelectValuesConverter.ts
New InsertQuerySelectValuesConverter class with toSelectUnion and toValues static methods and a flattenSelectQueries helper; validates shapes, maps columns↔values, and emits errors for unsupported structures or mismatches.
SQL printing for MERGE
packages/core/src/parsers/SqlPrintTokenParser.ts, packages/core/src/models/SqlPrintToken.ts
Adds new SqlPrintTokenContainerTypes for MERGE query elements; adds printer visitors for MERGE syntax constructs (MergeQuery, MergeWhenClause, MergeUpdateAction, MergeDeleteAction, MergeInsertAction, MergeDoNothingAction).
Public package exports
packages/core/src/index.ts
Adds re-export for InsertQuerySelectValuesConverter to public API surface.
Tests
packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts, packages/core/tests/transformers/buildDeleteQuery.test.ts, packages/core/tests/transformers/buildInsertQuery.test.ts, packages/core/tests/transformers/buildMergeQuery.test.ts, packages/core/tests/transformers/buildUpdateQuery.test.ts
Adds comprehensive tests for conversions and QueryBuilder behavior, including round-trip conversions, exact SQL assertions, and error scenarios for missing aliases/columns/unsupported clauses. Tightens assertions in UPDATE tests with exact string matching.

Sequence Diagram(s)

sequenceDiagram
    participant Dev
    participant SelectQ as SelectQuery\n(Simple/Binary/Values)
    participant QB as QueryBuilder
    participant Conv as InsertQuerySelectValuesConverter
    participant Result as Result Query

    Dev->>SelectQ: call toInsert/Update/Delete/Merge(options)
    SelectQ->>QB: buildInsert/Update/Delete/Merge(select, options)
    QB->>QB: normalize options\ninfer & validate columns
    QB->>Result: assemble target query\n(Insert/Update/Delete/Merge)
    Result-->>Dev: return built query

    Note over Conv,QB: Converter used for VALUES ↔ SELECT\ntransformations when invoked explicitly
    Dev->>Conv: toSelectUnion / toValues(insertQuery)
    Conv->>Conv: validate shape, flatten selects,\nmap columns↔tuples
    Conv-->>Dev: return transformed InsertQuery
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰
I hopped through selects with a nimble cheer,
Turned VALUES to UNION and INSERTs appear,
UPDATE, DELETE, MERGE — all dressed for the ball,
Columns aligned, aliases standing tall,
A rabbit’s small patchwork of SQL for all.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: Enhance QueryBuilder with DELETE and MERGE query support" accurately reflects the primary objective stated in the PR description, which explicitly emphasizes adding DELETE and MERGE query building capabilities to the QueryBuilder. The title is specific and clear about what is being enhanced (QueryBuilder) and what enhancements are being introduced (DELETE and MERGE support). While the PR also includes related changes such as conversion method additions across select query types and the new InsertQuerySelectValuesConverter, these are supportive changes that enable the main QueryBuilder enhancements rather than the core focus of the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/select-query-generalization

Warning

Tools execution failed with the following error:

Failed to run tools: 14 UNAVAILABLE: read ECONNRESET


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (16)
packages/core/tests/transformers/buildInsertQuery.test.ts (1)

7-7: Add one more negative test for wildcard/unnamed columns.

Add a case like SELECT * FROM t or SELECT 1 (no alias) to assert the error: “Columns cannot be inferred from the selectQuery...”. This guards regressions in column inference. Based on learnings.

packages/core/tests/transformers/buildDeleteQuery.test.ts (1)

7-7: Suggest a couple of extra cases for resilience.

  • CTE hoisting: WITH c AS (...) SELECT ... → ensure WITH is promoted above DELETE.
  • Composite PKs and duplicate column names in columns: verify de-duplication and predicate order.
  • Omit sourceAlias to verify default 'src'.
packages/core/tests/transformers/buildUpdateQuery.test.ts (1)

7-7: Add one more negative test and a tiny cleanup.

  • Negative: when the SELECT only contains primary keys (no non-PK columns), expect “No updatable columns found...”. This locks the boundary.
  • Optional: extract a small formatSql(q) helper to reduce repetition.
packages/core/tests/transformers/buildMergeQuery.test.ts (2)

8-8: Optionally add one end-to-end SQL assertion.

Add a single case that formats a MERGE to SQL via SqlFormatter and asserts the string. It complements the AST checks and catches formatter regressions. Based on learnings.


113-124: Future polish: clarify the insert‑columns error message.

When insertColumns are provided but none match the SELECT output, the current error reads as if columns weren’t provided. Consider a distinct message (e.g., “Provided insertColumns do not exist in selectQuery output”). Not a blocker; test can remain as is.

packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts (1)

8-17: Add two more negative cases for stricter guarantees.

  • SELECT→VALUES with FROM or WHERE should throw (guard already exists).
  • VALUES→SELECT with a tuple whose length doesn’t match the column list should throw.
packages/core/src/models/SimpleSelectQuery.ts (1)

133-147: Document side effects of conversion methods (they may reorder/mutate selectClause)

QueryBuilder’s builders can mutate the current SimpleSelectQuery (e.g., column reordering). Add a brief note to each toXQuery JSDoc, or consider invoking the builders on a defensive clone to avoid surprising changes to this.

packages/core/src/models/ValuesQuery.ts (1)

1-19: Remove unused import

ParameterCollector isn’t used in this file; drop it to keep imports clean.

-import { ParameterCollector } from "../transformers/ParameterCollector";
packages/core/src/models/BinarySelectQuery.ts (2)

2-9: Drop unused type import

CTEOptions isn’t used here.

-import type {
-    SelectQuery,
-    CTEOptions,
-    InsertQueryConversionOptions,
-    UpdateQueryConversionOptions,
-    DeleteQueryConversionOptions,
-    MergeQueryConversionOptions
-} from "./SelectQuery";
+import type {
+    SelectQuery,
+    InsertQueryConversionOptions,
+    UpdateQueryConversionOptions,
+    DeleteQueryConversionOptions,
+    MergeQueryConversionOptions
+} from "./SelectQuery";

14-14: Remove unused import

ParameterCollector is not referenced.

-import { ParameterCollector } from "../transformers/ParameterCollector";
packages/core/src/transformers/InsertQuerySelectValuesConverter.ts (3)

1-7: Prefer type-only import and direct model imports to avoid cycles

Use a type-only import for SelectQuery, and import SimpleSelectQuery / BinarySelectQuery from their modules to reduce barrel-induced cycles.

-import { SimpleSelectQuery, BinarySelectQuery } from "../models/SelectQuery";
+import { SimpleSelectQuery } from "../models/SimpleSelectQuery";
+import { BinarySelectQuery } from "../models/BinarySelectQuery";
-import { SelectQuery } from "../models/SelectQuery";
+import type { SelectQuery } from "../models/SelectQuery";

79-102: Strengthen SELECT→VALUES validation and alias handling

  • Reject extra clauses beyond FROM/WHERE (e.g., GROUP BY, HAVING, WINDOW, ORDER, LIMIT/OFFSET, FETCH, FOR).
  • Detect duplicate aliases; require a 1:1 mapping to columnNames.
-        const tuples = simpleQueries.map(query => {
-            if (query.fromClause || (query.whereClause && query.whereClause.condition)) {
-                throw new Error("SELECT queries with FROM or WHERE clauses cannot be converted to VALUES.");
-            }
+        const tuples = simpleQueries.map(query => {
+            const hasProhibitedClauses =
+                !!query.fromClause ||
+                !!query.whereClause ||
+                !!query.groupByClause ||
+                !!query.havingClause ||
+                !!query.windowClause ||
+                !!query.orderByClause ||
+                !!query.limitClause ||
+                !!query.offsetClause ||
+                !!query.fetchClause ||
+                !!query.forClause;
+            if (hasProhibitedClauses) {
+                throw new Error("SELECT queries with FROM/WHERE/GROUP/HAVING/WINDOW/ORDER/LIMIT/OFFSET/FETCH/FOR cannot be converted to VALUES.");
+            }
             const valueMap = new Map<string, ValueComponent>();
             for (const item of query.selectClause.items) {
                 const identifier = item.identifier?.name ?? null;
                 if (!identifier) {
                     throw new Error("Each SELECT item must have an alias matching target columns.");
                 }
-                if (!valueMap.has(identifier)) {
-                    valueMap.set(identifier, item.value);
-                }
+                if (valueMap.has(identifier)) {
+                    throw new Error(`Duplicate SELECT item alias '${identifier}'.`);
+                }
+                valueMap.set(identifier, item.value);
             }
+            if (valueMap.size !== columnNames.length) {
+                throw new Error("SELECT item aliases must match the target column list 1:1.");
+            }

17-24: Clarify error when selectQuery is null vs not a VALUES query

If insertQuery.selectQuery is null, the current message (“not a VALUES query”) can mislead. Consider a more precise error for null/undefined.

-        if (!(valuesQuery instanceof ValuesQuery)) {
-            throw new Error("InsertQuery selectQuery is not a VALUES query.");
-        }
+        if (!valuesQuery) {
+            throw new Error("InsertQuery has no selectQuery to convert.");
+        }
+        if (!(valuesQuery instanceof ValuesQuery)) {
+            throw new Error("InsertQuery.selectQuery is not a VALUES query.");
+        }
packages/core/src/transformers/QueryBuilder.ts (3)

40-46: Normalize after building the full binary tree (CTE hoisting completeness).

normalize is called before appending later queries, so CTEs introduced by subsequent queries may be missed. Normalize once after the loop.

Apply this diff:

-        CTENormalizer.normalize(result);
@@
-        for (let i = 2; i < queries.length; i++) {
+        for (let i = 2; i < queries.length; i++) {
             result.appendSelectQuery(operator, wrap(queries[i]));
         }
 
-        return result;
+        // Normalize after the full tree is built (captures CTEs from all branches).
+        result = CTENormalizer.normalize(result) as BinarySelectQuery;
+        return result;

253-255: Guard against alias collision (target vs source) and clarify the error message.

If sourceAlias equals target alias/name, generated predicates and column refs become ambiguous. Also, the Update error message mentions alias even though a table name fallback is accepted.

Apply this diff:

@@
-        if (!targetAlias) {
-            throw new Error(`Source expression does not have an alias. Please provide an alias for the source expression.`);
-        }
+        if (!targetAlias) {
+            throw new Error(`Unable to resolve target alias/name from source expression.`);
+        }
+        if (targetAlias === options.sourceAlias) {
+            throw new Error(`sourceAlias ('${options.sourceAlias}') must differ from target alias/name ('${targetAlias}').`);
+        }
@@
-        if (!targetAlias) {
-            throw new Error(`Source expression does not have an alias. Please provide an alias for the delete target.`);
-        }
+        if (!targetAlias) {
+            throw new Error(`Source expression must have an explicit alias for DELETE USING.`);
+        }
+        if (targetAlias === normalized.sourceAlias) {
+            throw new Error(`sourceAlias ('${normalized.sourceAlias}') must differ from target alias ('${targetAlias}').`);
+        }
@@
-        if (!targetAlias) {
-            throw new Error(`Source expression does not have an alias. Please provide an alias for the merge target.`);
-        }
+        if (!targetAlias) {
+            throw new Error(`Source expression must have an explicit alias for MERGE.`);
+        }
+        if (targetAlias === normalized.sourceAlias) {
+            throw new Error(`sourceAlias ('${normalized.sourceAlias}') must differ from target alias ('${targetAlias}').`);
+        }

Also applies to: 287-289, 321-323


682-684: Be explicit about CTEDisabler side‑effect.

Assign the returned query to make the mutation explicit and future‑proof if CTEDisabler switches to immutable behavior.

Apply this diff:

-        const cteDisabler = new CTEDisabler();
-        cteDisabler.execute(selectQuery);
+        const cteDisabler = new CTEDisabler();
+        selectQuery = cteDisabler.execute(selectQuery) as SimpleSelectQuery;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0f0b5b and 1186318.

📒 Files selected for processing (11)
  • packages/core/src/models/BinarySelectQuery.ts (2 hunks)
  • packages/core/src/models/SelectQuery.ts (3 hunks)
  • packages/core/src/models/SimpleSelectQuery.ts (2 hunks)
  • packages/core/src/models/ValuesQuery.ts (2 hunks)
  • packages/core/src/transformers/InsertQuerySelectValuesConverter.ts (1 hunks)
  • packages/core/src/transformers/QueryBuilder.ts (3 hunks)
  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts (1 hunks)
  • packages/core/tests/transformers/buildDeleteQuery.test.ts (1 hunks)
  • packages/core/tests/transformers/buildInsertQuery.test.ts (1 hunks)
  • packages/core/tests/transformers/buildMergeQuery.test.ts (1 hunks)
  • packages/core/tests/transformers/buildUpdateQuery.test.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
packages/core/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

packages/core/**/*.{ts,tsx}: Always compile and fix TypeScript errors before running tests
Source code comments must be written in English only
For model-driven mappings (mapping.typeInfo and mapping.structure), use convertModelDrivenMapping
Use imports from 'rawsql-ts' rather than local relative paths for library usage

Files:

  • packages/core/tests/transformers/buildMergeQuery.test.ts
  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts
  • packages/core/tests/transformers/buildInsertQuery.test.ts
  • packages/core/src/transformers/InsertQuerySelectValuesConverter.ts
  • packages/core/tests/transformers/buildUpdateQuery.test.ts
  • packages/core/src/models/SelectQuery.ts
  • packages/core/src/models/BinarySelectQuery.ts
  • packages/core/src/models/SimpleSelectQuery.ts
  • packages/core/tests/transformers/buildDeleteQuery.test.ts
  • packages/core/src/transformers/QueryBuilder.ts
  • packages/core/src/models/ValuesQuery.ts
packages/core/**/*.{test,spec}.{ts,tsx,js}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Tests are specs — never change expected values without user consultation

Files:

  • packages/core/tests/transformers/buildMergeQuery.test.ts
  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts
  • packages/core/tests/transformers/buildInsertQuery.test.ts
  • packages/core/tests/transformers/buildUpdateQuery.test.ts
  • packages/core/tests/transformers/buildDeleteQuery.test.ts
packages/core/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Remove console.log statements before commit

Files:

  • packages/core/tests/transformers/buildMergeQuery.test.ts
  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts
  • packages/core/tests/transformers/buildInsertQuery.test.ts
  • packages/core/src/transformers/InsertQuerySelectValuesConverter.ts
  • packages/core/tests/transformers/buildUpdateQuery.test.ts
  • packages/core/src/models/SelectQuery.ts
  • packages/core/src/models/BinarySelectQuery.ts
  • packages/core/src/models/SimpleSelectQuery.ts
  • packages/core/tests/transformers/buildDeleteQuery.test.ts
  • packages/core/src/transformers/QueryBuilder.ts
  • packages/core/src/models/ValuesQuery.ts
packages/core/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Use SqlFormatter for SQL comparison tests

Files:

  • packages/core/tests/transformers/buildMergeQuery.test.ts
  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts
  • packages/core/tests/transformers/buildInsertQuery.test.ts
  • packages/core/tests/transformers/buildUpdateQuery.test.ts
  • packages/core/tests/transformers/buildDeleteQuery.test.ts
🧠 Learnings (1)
📚 Learning: 2025-10-18T02:29:34.428Z
Learnt from: CR
PR: mk3008/rawsql-ts#0
File: packages/core/AGENTS.md:0-0
Timestamp: 2025-10-18T02:29:34.428Z
Learning: Applies to packages/core/**/*.{test,spec}.{ts,tsx} : Use SqlFormatter for SQL comparison tests

Applied to files:

  • packages/core/tests/transformers/buildInsertQuery.test.ts
  • packages/core/tests/transformers/buildUpdateQuery.test.ts
🧬 Code graph analysis (11)
packages/core/tests/transformers/buildMergeQuery.test.ts (5)
packages/core/src/parsers/SelectQueryParser.ts (1)
  • SelectQueryParser (31-669)
packages/core/src/models/SimpleSelectQuery.ts (1)
  • SimpleSelectQuery (44-733)
packages/core/src/transformers/QueryBuilder.ts (1)
  • QueryBuilder (19-720)
packages/core/src/models/MergeQuery.ts (4)
  • MergeUpdateAction (11-21)
  • MergeInsertAction (33-51)
  • MergeDoNothingAction (53-55)
  • MergeDeleteAction (23-31)
packages/core/src/models/ValueComponent.ts (2)
  • ColumnReference (47-82)
  • ValueList (38-45)
packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts (2)
packages/core/src/parsers/InsertQueryParser.ts (1)
  • InsertQueryParser (14-106)
packages/core/src/transformers/InsertQuerySelectValuesConverter.ts (1)
  • InsertQuerySelectValuesConverter (12-125)
packages/core/tests/transformers/buildInsertQuery.test.ts (3)
packages/core/src/parsers/SelectQueryParser.ts (1)
  • SelectQueryParser (31-669)
packages/core/src/models/SimpleSelectQuery.ts (1)
  • SimpleSelectQuery (44-733)
packages/core/src/transformers/QueryBuilder.ts (1)
  • QueryBuilder (19-720)
packages/core/src/transformers/InsertQuerySelectValuesConverter.ts (5)
packages/core/src/models/ValuesQuery.ts (1)
  • ValuesQuery (23-70)
packages/core/src/models/SimpleSelectQuery.ts (1)
  • SimpleSelectQuery (44-733)
packages/core/src/models/Clause.ts (3)
  • SelectItem (6-15)
  • SelectClause (17-28)
  • identifier (170-172)
packages/core/src/models/BinarySelectQuery.ts (1)
  • BinarySelectQuery (36-216)
packages/core/src/models/ValueComponent.ts (2)
  • ValueComponent (5-27)
  • TupleExpression (396-403)
packages/core/tests/transformers/buildUpdateQuery.test.ts (2)
packages/core/src/parsers/SelectQueryParser.ts (1)
  • SelectQueryParser (31-669)
packages/core/src/transformers/QueryBuilder.ts (1)
  • QueryBuilder (19-720)
packages/core/src/models/SelectQuery.ts (2)
packages/core/src/models/ValueComponent.ts (3)
  • name (120-122)
  • name (383-385)
  • SqlParameterValue (223-223)
packages/core/src/models/SimpleSelectQuery.ts (1)
  • SimpleSelectQuery (44-733)
packages/core/src/models/BinarySelectQuery.ts (1)
packages/core/src/models/SelectQuery.ts (9)
  • BinarySelectQuery (100-100)
  • InsertQueryConversionOptions (43-46)
  • InsertQuery (102-102)
  • UpdateQueryConversionOptions (48-53)
  • UpdateQuery (103-103)
  • DeleteQueryConversionOptions (55-60)
  • DeleteQuery (104-104)
  • MergeQueryConversionOptions (66-75)
  • MergeQuery (105-105)
packages/core/src/models/SimpleSelectQuery.ts (2)
packages/core/src/models/SelectQuery.ts (9)
  • SelectQuery (88-97)
  • InsertQueryConversionOptions (43-46)
  • InsertQuery (102-102)
  • UpdateQueryConversionOptions (48-53)
  • UpdateQuery (103-103)
  • DeleteQueryConversionOptions (55-60)
  • DeleteQuery (104-104)
  • MergeQueryConversionOptions (66-75)
  • MergeQuery (105-105)
packages/core/src/transformers/QueryBuilder.ts (1)
  • QueryBuilder (19-720)
packages/core/tests/transformers/buildDeleteQuery.test.ts (2)
packages/core/src/models/SimpleSelectQuery.ts (1)
  • SimpleSelectQuery (44-733)
packages/core/src/transformers/QueryBuilder.ts (1)
  • QueryBuilder (19-720)
packages/core/src/transformers/QueryBuilder.ts (9)
packages/core/src/models/SelectQuery.ts (9)
  • SimpleSelectQuery (99-99)
  • InsertQueryConversionOptions (43-46)
  • InsertQuery (102-102)
  • UpdateQueryConversionOptions (48-53)
  • UpdateQuery (103-103)
  • DeleteQueryConversionOptions (55-60)
  • DeleteQuery (104-104)
  • MergeQueryConversionOptions (66-75)
  • MergeQuery (105-105)
packages/core/src/models/SimpleSelectQuery.ts (1)
  • SimpleSelectQuery (44-733)
packages/core/src/parsers/SourceExpressionParser.ts (1)
  • SourceExpressionParser (7-72)
packages/core/src/models/Clause.ts (10)
  • InsertClause (576-589)
  • UpdateClause (541-557)
  • SetClauseItem (497-539)
  • SetClause (476-483)
  • FromClause (306-336)
  • WhereClause (49-56)
  • DeleteClause (559-569)
  • UsingClause (338-348)
  • SelectItem (6-15)
  • WithClause (370-381)
packages/core/src/models/MergeQuery.ts (5)
  • MergeWhenClause (57-69)
  • MergeUpdateAction (11-21)
  • MergeDeleteAction (23-31)
  • MergeDoNothingAction (53-55)
  • MergeInsertAction (33-51)
packages/core/src/models/ValueComponent.ts (3)
  • ValueList (38-45)
  • ColumnReference (47-82)
  • BinaryExpression (198-209)
packages/core/src/transformers/SelectValueCollector.ts (1)
  • SelectValueCollector (13-269)
packages/core/src/transformers/CTECollector.ts (1)
  • CTECollector (25-501)
packages/core/src/transformers/CTEDisabler.ts (1)
  • CTEDisabler (25-443)
packages/core/src/models/ValuesQuery.ts (2)
packages/core/src/models/SelectQuery.ts (9)
  • SimpleSelectQuery (99-99)
  • InsertQueryConversionOptions (43-46)
  • InsertQuery (102-102)
  • UpdateQueryConversionOptions (48-53)
  • UpdateQuery (103-103)
  • DeleteQueryConversionOptions (55-60)
  • DeleteQuery (104-104)
  • MergeQueryConversionOptions (66-75)
  • MergeQuery (105-105)
packages/core/src/transformers/QueryBuilder.ts (1)
  • QueryBuilder (19-720)
🔇 Additional comments (11)
packages/core/tests/transformers/buildInsertQuery.test.ts (1)

8-20: LGTM: solid coverage for INSERT conversions and CTE hoisting.

  • CTE hoist + options path, inferred columns, optional column dropping, explicit list filtering, and no-overlap error are all asserted precisely with SqlFormatter output. Good use of exact-equality for formatter-stable strings.

Also applies to: 22-29, 31-41, 43-53, 55-62

packages/core/tests/transformers/buildDeleteQuery.test.ts (1)

8-19: LGTM: DELETE with PKs and additional match columns is correctly asserted.

USING source aliasing and equality predicates look right; strings match formatter output.

Also applies to: 21-33

packages/core/tests/transformers/buildUpdateQuery.test.ts (1)

8-19: LGTM: comprehensive UPDATE coverage (CTE, aliasing, composite PKs, column selection, errors).

Legacy signature, options path, and error cases are well exercised with precise SqlFormatter assertions.

Also applies to: 21-33, 35-45, 47-58, 60-75, 77-89, 91-103, 105-114, 116-125

packages/core/tests/transformers/buildMergeQuery.test.ts (1)

9-41: LGTM: MERGE actions and column planning validated at AST level with good breadth.

Aliases, action kinds, update/set items, insert columns/values, and error paths are covered.

Also applies to: 43-64, 66-82, 84-100, 102-111, 113-124

packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts (1)

18-26: LGTM: converter round‑trip and error coverage look solid with normalized SQL checks.

Factory helper keeps the formatter usage tidy; assertions are precise.

Also applies to: 28-36, 38-47, 49-54, 56-61

packages/core/src/models/SimpleSelectQuery.ts (1)

129-131: UNION ALL helper looks good

Consistent with existing toBinaryQuery delegation. No issues.

packages/core/src/models/ValuesQuery.ts (1)

41-59: Delegations LGTM

Using toSimpleQuery() as a funnel point is clean and consistent.

packages/core/src/models/BinarySelectQuery.ts (2)

168-171: exceptAllRaw helper LGTM

Matches the pattern used by other raw helpers.


173-187: New conversion helpers LGTM

Delegation to toSimpleQuery() keeps behavior consistent across query variants.

packages/core/src/models/SelectQuery.ts (1)

88-97: Interface extension LGTM

Unified toXQuery surface across query variants is clear and consistent.

packages/core/src/transformers/InsertQuerySelectValuesConverter.ts (1)

52-57: No issues found—code uses correct constructor key

The verification confirms the constructor accepts returning as the parameter key (line 24 of InsertQuery.ts), and the code correctly passes returning: insertQuery.returningClause. The field assignment this.returningClause = params.returning ?? null; (line 30) handles the mapping internally.

…narySelectQuery, SimpleSelectQuery, and ValuesQuery; enhance QueryBuilder with conversion methods; update SqlPrintTokenParser for new query types
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/core/src/transformers/QueryBuilder.ts (1)

543-564: Consider clarifying the matchColumns logic.

The logic at line 552 filters explicit columns to exclude primary keys, then lines 556-557 add primary keys separately. While correct, the two-step approach (exclude PKs from matchColumns, then add PKs to requiredColumns) could be clearer with a brief comment explaining that explicit columns provide additional matching criteria beyond the always-required primary keys.

Consider adding a clarifying comment:

         let matchColumns: string[] = [];
         if (explicitColumns && explicitColumns.length > 0) {
             const normalized = QueryBuilder.normalizeColumnArray(explicitColumns);
             const preferred = new Set(normalized);
+            // Explicit columns add extra match criteria; PKs are always included separately
             matchColumns = selectColumns.filter(name => preferred.has(name) && !primaryKeySet.has(name));
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e97f8d7 and 350c76a.

📒 Files selected for processing (9)
  • packages/core/src/index.ts (1 hunks)
  • packages/core/src/models/BinarySelectQuery.ts (2 hunks)
  • packages/core/src/models/SimpleSelectQuery.ts (2 hunks)
  • packages/core/src/models/SqlPrintToken.ts (1 hunks)
  • packages/core/src/models/ValuesQuery.ts (2 hunks)
  • packages/core/src/parsers/SqlPrintTokenParser.ts (3 hunks)
  • packages/core/src/transformers/QueryBuilder.ts (3 hunks)
  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts (1 hunks)
  • packages/core/tests/transformers/buildMergeQuery.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/core/src/models/ValuesQuery.ts
  • packages/core/src/models/SimpleSelectQuery.ts
  • packages/core/tests/transformers/buildMergeQuery.test.ts
🧰 Additional context used
📓 Path-based instructions (4)
packages/core/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

packages/core/**/*.{ts,tsx}: Always compile and fix TypeScript errors before running tests
Source code comments must be written in English only
For model-driven mappings (mapping.typeInfo and mapping.structure), use convertModelDrivenMapping
Use imports from 'rawsql-ts' rather than local relative paths for library usage

Files:

  • packages/core/src/index.ts
  • packages/core/src/models/SqlPrintToken.ts
  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts
  • packages/core/src/transformers/QueryBuilder.ts
  • packages/core/src/parsers/SqlPrintTokenParser.ts
  • packages/core/src/models/BinarySelectQuery.ts
packages/core/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Remove console.log statements before commit

Files:

  • packages/core/src/index.ts
  • packages/core/src/models/SqlPrintToken.ts
  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts
  • packages/core/src/transformers/QueryBuilder.ts
  • packages/core/src/parsers/SqlPrintTokenParser.ts
  • packages/core/src/models/BinarySelectQuery.ts
packages/core/**/*.{test,spec}.{ts,tsx,js}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Tests are specs — never change expected values without user consultation

Files:

  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts
packages/core/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Use SqlFormatter for SQL comparison tests

Files:

  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts
🧠 Learnings (1)
📚 Learning: 2025-10-18T02:29:34.428Z
Learnt from: CR
PR: mk3008/rawsql-ts#0
File: packages/core/AGENTS.md:0-0
Timestamp: 2025-10-18T02:29:34.428Z
Learning: Applies to packages/core/**/*.{test,spec}.{ts,tsx} : Use SqlFormatter for SQL comparison tests

Applied to files:

  • packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts
🧬 Code graph analysis (4)
packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts (3)
packages/core/src/parsers/InsertQueryParser.ts (1)
  • InsertQueryParser (14-106)
packages/core/src/transformers/InsertQuerySelectValuesConverter.ts (1)
  • InsertQuerySelectValuesConverter (13-126)
packages/core/src/transformers/QueryBuilder.ts (1)
  • QueryBuilder (20-727)
packages/core/src/transformers/QueryBuilder.ts (9)
packages/core/src/models/SimpleSelectQuery.ts (1)
  • SimpleSelectQuery (44-753)
packages/core/src/models/SelectQuery.ts (9)
  • SimpleSelectQuery (99-99)
  • InsertQueryConversionOptions (43-46)
  • InsertQuery (102-102)
  • UpdateQueryConversionOptions (48-53)
  • UpdateQuery (103-103)
  • DeleteQueryConversionOptions (55-60)
  • DeleteQuery (104-104)
  • MergeQueryConversionOptions (66-75)
  • MergeQuery (105-105)
packages/core/src/parsers/SourceExpressionParser.ts (1)
  • SourceExpressionParser (7-72)
packages/core/src/models/Clause.ts (10)
  • InsertClause (576-589)
  • UpdateClause (541-557)
  • SetClauseItem (497-539)
  • SetClause (476-483)
  • FromClause (306-336)
  • WhereClause (49-56)
  • DeleteClause (559-569)
  • UsingClause (338-348)
  • SelectItem (6-15)
  • WithClause (370-381)
packages/core/src/transformers/InsertQuerySelectValuesConverter.ts (1)
  • InsertQuerySelectValuesConverter (13-126)
packages/core/src/models/ValueComponent.ts (3)
  • ValueList (38-45)
  • ColumnReference (47-82)
  • BinaryExpression (198-209)
packages/core/src/transformers/SelectValueCollector.ts (1)
  • SelectValueCollector (13-269)
packages/core/src/transformers/CTECollector.ts (1)
  • CTECollector (25-501)
packages/core/src/transformers/CTEDisabler.ts (1)
  • CTEDisabler (25-443)
packages/core/src/parsers/SqlPrintTokenParser.ts (2)
packages/core/src/models/MergeQuery.ts (6)
  • MergeWhenClause (57-69)
  • MergeUpdateAction (11-21)
  • MergeDeleteAction (23-31)
  • MergeInsertAction (33-51)
  • MergeDoNothingAction (53-55)
  • MergeMatchType (5-5)
packages/core/src/models/SqlPrintToken.ts (1)
  • SqlPrintToken (96-126)
packages/core/src/models/BinarySelectQuery.ts (1)
packages/core/src/models/SelectQuery.ts (8)
  • InsertQueryConversionOptions (43-46)
  • InsertQuery (102-102)
  • UpdateQueryConversionOptions (48-53)
  • UpdateQuery (103-103)
  • DeleteQueryConversionOptions (55-60)
  • DeleteQuery (104-104)
  • MergeQueryConversionOptions (66-75)
  • MergeQuery (105-105)
🔇 Additional comments (25)
packages/core/src/models/SqlPrintToken.ts (1)

86-91: LGTM! Clean addition of MERGE container types.

The new enum members are well-named, follow the existing string-valued pattern, and are logically positioned to support MERGE query formatting.

packages/core/src/parsers/SqlPrintTokenParser.ts (8)

43-43: LGTM! Import statement is complete and correct.

All necessary MERGE-related types are imported from the appropriate module.


314-319: LGTM! Handler registration is correct.

All MERGE-related types are properly registered with their corresponding visitor methods, following the established pattern.


2393-2420: LGTM! MERGE query rendering is correct.

The visitor properly constructs the MERGE INTO syntax with optional WITH clause, handles target/source/condition, and iterates through WHEN clauses with appropriate spacing.


2422-2439: LGTM! WHEN clause rendering is correct.

Properly converts match types to keywords, handles optional AND conditions, and renders the THEN action with appropriate spacing.


2441-2466: LGTM! UPDATE and DELETE actions are correctly implemented.

Both visitor methods properly render their respective actions with appropriate keywords, clauses, and spacing. Optional WHERE clause handling is consistent.


2468-2500: LGTM! INSERT action rendering is correct.

The visitor properly handles:

  • Optional column list with parentheses
  • DEFAULT VALUES keyword
  • VALUES clause with parentheses
  • Appropriate spacing and separators

The implementation correctly uses early return for DEFAULT VALUES and follows the established token construction patterns.


2502-2504: LGTM! DO NOTHING action is correctly implemented.

Simple and appropriate implementation. The underscore prefix for the unused parameter follows TypeScript conventions.


2506-2519: LGTM! Match type conversion is comprehensive.

The switch statement correctly maps all MERGE match types to their SQL keywords with a safe default fallback. The keywords follow standard MERGE syntax.

packages/core/src/index.ts (1)

45-45: LGTM! Public API export is appropriate.

The export correctly exposes the new InsertQuerySelectValuesConverter class, making it available to package consumers as part of the enhanced query conversion capabilities.

packages/core/tests/transformers/InsertQuerySelectValuesConverter.test.ts (2)

1-8: LGTM! Test setup follows coding guidelines.

The imports are appropriate and the formatter setup correctly uses SqlFormatter as required by the coding guidelines for SQL comparison tests.

Based on learnings.


9-101: Excellent test coverage!

The test suite is comprehensive, covering:

  • Bidirectional conversions (VALUES ↔ SELECT UNION ALL)
  • Round-trip conversion without data loss
  • Error scenarios (missing column lists, missing aliases, FROM clauses, tuple mismatches)
  • Integration with QueryBuilder public API

All tests properly use SqlFormatter for normalized SQL comparisons and include clear, descriptive test names.

packages/core/src/models/BinarySelectQuery.ts (2)

1-19: LGTM! Imports are properly structured.

Type-only imports for conversion options and query types are correctly used, which aids tree-shaking and prevents circular dependency issues.


171-201: Clean delegation pattern for query conversions.

The four new conversion methods (toInsertQuery, toUpdateQuery, toDeleteQuery, toMergeQuery) follow a consistent pattern by delegating to toSimpleQuery() before applying the conversion. The JSDoc comments appropriately note that SELECT list reordering may occur, which is important for users to understand.

packages/core/src/transformers/QueryBuilder.ts (11)

228-242: LGTM! Clean delegation to helper methods.

The updated buildInsertQuery properly delegates to normalization and preparation helpers before constructing the InsertQuery. The comment clearly explains the flow: normalize options → prepare columns → extract CTEs → build query.


249-260: LGTM! Thin wrappers for converter methods.

The convertInsertValuesToSelect and convertInsertSelectToValues methods cleanly delegate to InsertQuerySelectValuesConverter, providing a consistent QueryBuilder API surface for users.


265-295: LGTM! Well-structured UPDATE query builder.

The method properly:

  • Normalizes input options (backward compatibility maintained)
  • Validates target alias presence
  • Extracts CTEs for cleaner SQL
  • Constructs SET items from prepared columns
  • Builds equality predicates for the WHERE clause

Error messages are clear and guide users toward resolution.


300-321: LGTM! DELETE query builder follows consistent pattern.

The implementation correctly:

  • Normalizes options and prepares predicate columns
  • Validates target alias requirement
  • Extracts CTEs
  • Builds USING clause with the source query
  • Constructs WHERE predicate for matching rows

326-396: LGTM! Comprehensive MERGE query builder.

The implementation handles the complexity of MERGE operations well:

  • Prepares separate column sets for update/insert actions
  • Validates target alias presence
  • Constructs appropriate WHEN clauses based on action types (update/delete/doNothing)
  • Validates that at least one action is generated
  • Provides clear error messages for missing columns

The logic for handling the three MERGE scenarios (matched, not_matched, not_matched_by_source) is correct and complete.


398-461: LGTM! Robust input normalization.

The normalization helper methods properly:

  • Maintain backward compatibility with legacy signatures (especially normalizeUpdateOptions)
  • Provide sensible defaults (sourceAlias: 'src')
  • Validate inputs (normalizeColumnArray ensures non-empty arrays)
  • Return consistent, fully-normalized option objects

This defensive approach prevents downstream errors and provides clear validation messages.


496-511: LGTM! INSERT column preparation preserves explicit ordering.

The implementation correctly:

  • Validates all explicit columns exist in the select list (lines 501-503)
  • Deduplicates while preserving order (line 500)
  • Reorders the SELECT clause to match caller-provided column order (line 505)
  • Falls back to select list order when explicit columns not provided

This addresses the past review feedback about honoring caller-provided column order.


513-541: LGTM! UPDATE column preparation with proper PK handling.

The implementation correctly:

  • Ensures primary keys exist in the select list (line 515)
  • Filters out PKs from update candidates (line 518)
  • Preserves explicit column order when provided (lines 521-528)
  • Validates explicit columns aren't PKs and exist in candidates (lines 524-527)
  • Reorders SELECT with PKs first, then update columns (lines 533-537)

This addresses the past review feedback about explicit column ordering.


566-630: LGTM! MERGE column preparation with comprehensive validation.

The implementation correctly:

  • Validates all explicit columns (update and insert) exist in select list (lines 585-587, 600-602)
  • Preserves explicit column order for both update and insert actions (lines 589, 604)
  • Deduplicates while maintaining order (lines 584, 599)
  • Builds a comprehensive desiredOrder that prioritizes PKs, then action-specific columns (lines 610-615)
  • Returns separate column lists based on the actual action types (lines 619-629)

This addresses the past review feedback about preserving explicit ordering in MERGE operations.


632-663: LGTM! SELECT clause reordering with validation.

The implementation correctly:

  • Maps existing items by name for lookup (lines 633-642)
  • Rebuilds items in the desired order (lines 644-656)
  • Validates each requested column exists (lines 651-653)
  • Deduplicates using a seen Set (lines 645, 647-650, 655)
  • Directly mutates the selectClause.items (line 662), which is intentional for in-place reordering

665-726: LGTM! Clean utility methods.

The utility methods are well-implemented:

  • getSelectItemName: Extracts names from SelectItem with proper fallback logic
  • ensureSelectClauseSize: Defensive validation with clear error message
  • extractWithClause: Clean CTE extraction and removal using CTECollector/CTEDisabler
  • buildEqualityPredicate: Correctly chains equality comparisons with AND
  • toColumnReference: Simple factory for consistency
  • mergeUniqueColumns: Order-preserving deduplication

All methods have clear, single responsibilities and proper error handling.

@mk3008 mk3008 merged commit abd3cca into main Oct 24, 2025
13 checks passed
@mk3008 mk3008 deleted the feature/select-query-generalization branch October 24, 2025 12:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant